package com.kaifamiao.controller;

import com.kaifamiao.entity.RegularFile;
import com.kaifamiao.service.FileService;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@WebServlet("/file/*")
@MultipartConfig
public class FileServlet extends HttpServlet {

    private Map<String,String> causeMap;
    private FileService fileService;

    @Override
    public void init() throws ServletException {
        causeMap = new HashMap<>();
        causeMap.put( "A", "表单控件名称错误" );
        causeMap.put( "B", "请先选择文件后再提交" );
        fileService = new FileService();
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 设置向响应缓冲区输出数据时使用的字符编码
        response.setCharacterEncoding( "UTF-8" );

        final String method = request.getMethod();
        final String uri = request.getRequestURI();

        // 客户端以GET方式访问 `/file/list`
        if( "GET".equals( method ) && "/file/list".equals( uri ) ) {
            this.list(request, response);
            return;
        }

        // 客户端以GET方式访问 `/file/download`
        if( "GET".equals( method ) && "/file/download".equals( uri ) ) {
            this.download(request, response);
            return;
        }

        // 客户端以GET方式访问 `/file/remove`
        if( "GET".equals( method ) && "/file/remove".equals( uri ) ) {
            this.remove(request, response);
            return;
        }

        // 客户端以GET方式访问 `/file/upload`
        if( "GET".equals( method ) && "/file/upload".equals( uri ) ) {
            this.uploadPage(request, response);
            return;
        }

        // 客户端以POST方式访问 `/file/upload`
        if( "POST".equals( method ) && "/file/upload".equals( uri ) ) {
            this.uploadAction(request, response);
            return;
        }

    }

    private void list(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType( "text/html;charset=UTF-8" );
        PrintWriter out = response.getWriter();
        out.println( "<link rel='stylesheet' href='/css/general.css'>");
        List<RegularFile> list = fileService.loadAll();
        if( list == null || list.isEmpty() ) {
            out.println( "<p>暂无文件</p>");
            out.println( "<p> <a href='/file/upload'>上传文件</a> </p>" );
            return;
        }

        out.println( "<table class='tab'>" );
        out.println( "<tr>");
        out.println( "<th>序号</th>");
        out.println( "<th>文件名称</th>");
        out.println( "<th>创建时间</th>");
        out.println( "<th>用户操作</th>");
        out.println( "</tr>");
        for (int i = 0, n = list.size(); i < n; i++) {
            RegularFile rf = list.get( i );
            out.println( "<tr>");
            out.println( "<td>" + ( i + 1 ) +"</td>");
            out.println( "<td>" + rf.getName() +"</td>");
            out.println( "<td>" + rf.getUploadTime() +"</td>");
            out.println( "<td>");
            out.println( "<a href='/file/download?id=" + rf.getId() + "'>下载</a>");
            out.println( "<a href='/file/remove?id=" + rf.getId() + "'>删除</a>");
            out.println( "</td>");
            out.println( "</tr>");
        }
        out.println( "<tr>");
        out.println( "<td colspan='4'>");
        out.println( "<a href='/file/upload'>上传新文件</a>" );
        out.println( "</td>");
        out.println( "</tr>");
        out.println( "</table>" );

    }

    private void download(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 获得请求参数
        String id = request.getParameter( "id" );

        // 根据 id 查询文件信息

        // 根据 checksum 确定被下载的文件

        // 设置响应头中的`content-type`字段(即设置MIME类型)
        response.setContentType( "application/octet-stream" );

        // 对文件名称进行编码
        // URLEncoder.encode( name, StandardCharsets.UTF_8 );

        // 设置响应头中的`content-dispostion`字段

        // 通过字节输出流将文件内容输出到响应缓冲区
    }

    private void remove(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 获得请求参数
        String idString = request.getParameter( "id" );

        if( StringUtils.isEmpty( idString ) ) {
            response.sendRedirect( "/file/list" );
            return;
        }
        Integer id = Integer.valueOf( idString );
        // 根据 id 查询文件信息
        RegularFile rf = fileService.load( id );
        String checksum = rf.getChecksum();
        // 判断是否存在与当前文件 checksum 相同的其它文件信息
        boolean notExists = fileService.notExists( checksum, rf.getId() );

        // 除当前文件外，没有其它文件的 checksum 与当前文件的 checksum 相同
        if( notExists ) {
            String path = rf.getPath();
            String original = rf.getName();
            final String pathname = path + File.separator + fileService.naming( original, checksum );
            // 从磁盘上删除文件
            File f = new File( pathname );
            if( f.exists() ) { // 若磁盘上存在响应的文件
                f.delete(); // 则从磁盘上删除文件
            }
        }

        // 删除文件信息
        fileService.remove( id );

        // 以上操作完毕返回文件列表
        response.sendRedirect("/file/list");
    }

    private void uploadPage(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 获得请求参数中cause参数值
        final String cause = request.getParameter( "cause" );
        response.setContentType( "text/html;charset=UTF-8" );
        PrintWriter out = response.getWriter();
        out.println( "<!DOCTYPE html>" );
        out.println( "<html lang='en'>" );
        out.println( "<head>" );
        out.println( "<meta charset='UTF-8'>" );
        out.println( "<title>文件上传</title>" );
        out.println( "<link rel='stylesheet' href='/css/general.css'>" );
        out.println( "</head>" );
        out.println( "<body>" );
        out.println( "<div class='wrapper upload'>" );
        out.println( cause == null ? "" : ( "<p>" + causeMap.get( cause ) + "</p>" ) );
        out.println( "<form action='/file/upload' method='post' enctype='multipart/form-data'>" );
        out.println( "<span>文件上传:</span>" );
        out.println( "<input type='file' name='suibian'>" );
        out.println( "<button type='submit'>上传</button>" );
        out.println( "</form>" );
        out.println( "</div>" );
        out.println( "</body>" );
        out.println( "</html>" );
    }

    private void uploadAction(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 这里暂时将文件保存路径写死，未来应该通过配置文件来确定
        final String path = "D:/uploads";

        // <input type="file" name="suibian">
        Part part = request.getPart( "suibian" );
        if( part == null ){
            response.sendRedirect( "/file/upload?cause=A" );
            return;
        }

        final String original = part.getSubmittedFileName();
        if( StringUtils.isEmpty( original ) ) {
            response.sendRedirect( "/file/upload?cause=B" );
            return;
        }

        // 获得用户上传的文件内容
        InputStream in = part.getInputStream();
        byte[] bytes = in.readAllBytes();
        in.close();

        // 根据文件内容求校验和(checksum)
        String checksum = DigestUtils.sha1Hex( bytes );

        // 创建一个用于记录文件信息的实例
        RegularFile rf = new RegularFile();
        rf.setPath( path );
        rf.setName( original );
        rf.setChecksum( checksum );

        // 确定文件存储路径和名称
        final String pathname = path + File.separator + fileService.naming( original, checksum );

        File target = new File( pathname );
        // 判断用户上传的文件在服务器上是否已经存在
        if( !target.exists() ) {
            // 若文件不存在则将文件内容保存到指定目录下
            fileService.store(pathname, bytes);
        }

        // 不论文件是否已经存在，都需要将文件信息保存到数据库中
        fileService.store( rf );
        // 最后重定向到文件列表
        response.sendRedirect( "/file/list" );
    }

}
